package com.minimalist.common.config.exception;

import cn.dev33.satoken.exception.NotLoginException;
import cn.dev33.satoken.exception.NotPermissionException;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.alibaba.csp.sentinel.slots.block.authority.AuthorityException;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeException;
import com.alibaba.csp.sentinel.slots.block.flow.FlowException;
import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowException;
import com.minimalist.common.entity.basic.enums.RespEnum;
import com.minimalist.common.response.R;
import feign.FeignException;
import jakarta.validation.ConstraintViolation;
import jakarta.validation.ConstraintViolationException;
import net.dreamlu.mica.xss.core.XssException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.NestedExceptionUtils;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.method.annotation.MethodArgumentConversionNotSupportedException;

import java.util.List;
import java.util.Set;
import java.util.StringJoiner;

/**
 * 全局异常处理
 */
@RestControllerAdvice
public class GlobalExceptionHandler{

    private static final Logger log = LoggerFactory.getLogger(GlobalExceptionHandler.class);

    /**
     * 参数异常
     *
     * @param e 异常
     * @return 响应
     */
    @ExceptionHandler(value = {IllegalArgumentException.class, MethodArgumentNotValidException.class,
            ConstraintViolationException.class})
    public ResponseEntity<R<?>> paramsException(Exception e){
        switch (e){
            case IllegalArgumentException _ -> {
                log.warn(e.getMessage(), e);
                return ResponseEntity.status(HttpStatus.BAD_REQUEST)
                        .body(R.fail(RespEnum.PARAM_ERROR.getCode(), RespEnum.PARAM_ERROR.getDesc()));
            }
            case MethodArgumentNotValidException methodArgumentNotValidException -> {
                // 参数异常处理
                log.warn(e.getMessage(), e);
                BindingResult bindingResult = methodArgumentNotValidException.getBindingResult();
                List<FieldError> errors = bindingResult.getFieldErrors();
                StringJoiner sj = new StringJoiner("，");
                errors.forEach(error -> sj.add(error.getDefaultMessage()));
                return ResponseEntity.status(HttpStatus.BAD_REQUEST)
                        .body(R.fail(RespEnum.PARAM_ERROR.getCode(), sj.toString()));
            }
            case ConstraintViolationException constraintViolationException -> {
                // 参数异常处理
                log.warn(e.getMessage(), e);
                Set<ConstraintViolation<?>> constraintViolations = constraintViolationException
                        .getConstraintViolations();
                StringJoiner sj = new StringJoiner("，");
                constraintViolations.forEach(c -> sj.add(c.getMessageTemplate()));
                return ResponseEntity.status(HttpStatus.BAD_REQUEST)
                        .body(R.fail(RespEnum.PARAM_ERROR.getCode(), sj.toString()));
            }
            case null, default -> {
                return exception(e);
            }
        }
    }

    /**
     * 自定义异常
     *
     * @param e 异常
     * @return 响应
     */
    @ExceptionHandler(value = {BusinessException.class})
    public ResponseEntity<R<?>> customException(Exception e){
        // 业务异常
        if (e instanceof BusinessException businessException){
            // 记录日志
            log.error(e.getMessage(), e);
            // 响应 业务异常
            return ResponseEntity.status(HttpStatus.BAD_REQUEST)
                    .body(R.fail(businessException.getCode(), e.getMessage()));
        } else{
            return exception(e);
        }
    }

    /**
     * 未登录异常处理
     */
    @ExceptionHandler(value = {NotLoginException.class})
    public ResponseEntity<R<?>> customNotLoginException(Exception e){
        log.warn("未登录异常: {}", e.getMessage());
        return ResponseEntity.status(HttpStatus.UNAUTHORIZED)
                .body(R.fail(RespEnum.REQUEST_UNAUTH.getCode(), RespEnum.REQUEST_UNAUTH.getDesc()));
    }

    /**
     * 无权限异常处理
     */
    @ExceptionHandler(value = {NotPermissionException.class})
    public ResponseEntity<R<?>> customNotPermissionException(Exception e){
        log.warn("权限不足: {}", e.getMessage());
        return ResponseEntity.status(HttpStatus.FORBIDDEN)
                .body(R.fail(RespEnum.NO_OPERATION_PERMISSION.getCode(), RespEnum.NO_OPERATION_PERMISSION.getDesc()));
    }

    /**
     * XSS异常
     *
     * @param e 异常
     * @return 响应
     */
    @ExceptionHandler(value = {HttpMessageNotReadableException.class,
            MethodArgumentConversionNotSupportedException.class})
    public ResponseEntity<R<?>> xssException(Exception e){
        Throwable rootCause = NestedExceptionUtils.getRootCause(e);
        if (rootCause instanceof XssException){
            // 记录日志
            log.error("XSS攻击: {}", e.getMessage(), e);
            return ResponseEntity.status(HttpStatus.BAD_REQUEST)
                    .body(R.fail(RespEnum.PARAM_XSS_ERROR.getCode(), RespEnum.PARAM_XSS_ERROR.getDesc()));
        } else{
            return exception(e);
        }
    }

    /**
     * 未知异常
     *
     * @param e 异常
     * @return 响应
     */
    @ExceptionHandler(value = {Exception.class})
    public ResponseEntity<R<?>> exception(Exception e){
        // 记录日志
        log.error("系统异常: {}", e.getMessage(), e);
        // 响应 系统异常
        return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
                .body(R.fail(RespEnum.FAILED.getCode(), RespEnum.FAILED.getDesc()));
    }

    /**
     * 处理Feign相关异常
     */
    @ExceptionHandler({FeignException.class, feign.codec.DecodeException.class})
    public ResponseEntity<R<?>> handleFeignException(Exception e){
        if (e instanceof feign.codec.DecodeException){
            log.error("Feign解码异常: {}", e.getMessage(), e);
            // 处理解码异常
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
                    .body(R.fail(HttpStatus.INTERNAL_SERVER_ERROR.value(), "服务响应解析失败，请检查接口返回类型"));
        } else if (e instanceof FeignException feignException){
            log.error("Feign调用异常: {}", e.getMessage(), e);

            // 根据状态码处理不同类型的异常
            if (feignException.status() == 404){
                return ResponseEntity.status(HttpStatus.NOT_FOUND)
                        .body(R.fail(HttpStatus.NOT_FOUND.value(), "服务未找到，请稍后再试"));
            } else if (feignException.status() == 401 || feignException.status() == 403){
                return ResponseEntity.status(HttpStatus.UNAUTHORIZED)
                        .body(R.fail(HttpStatus.UNAUTHORIZED.value(), "权限不足"));
            }
        }

        return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
                .body(R.fail(HttpStatus.INTERNAL_SERVER_ERROR.value(), "服务调用失败，请稍后再试"));
    }

    /**
     * 处理Sentinel限流异常
     */
    @ExceptionHandler(BlockException.class)
    public ResponseEntity<R<?>> handleBlockException(BlockException e){
        if (e instanceof FlowException){
            log.warn("请求被限流: {}", e.getRule().getResource());
            return ResponseEntity.status(HttpStatus.TOO_MANY_REQUESTS)
                    .body(R.fail(HttpStatus.TOO_MANY_REQUESTS.value(), "请求过于频繁，请稍后再试"));
        } else if (e instanceof DegradeException){
            log.warn("服务被降级: {}", e.getRule().getResource());
            return ResponseEntity.status(HttpStatus.SERVICE_UNAVAILABLE)
                    .body(R.fail(HttpStatus.SERVICE_UNAVAILABLE.value(), "服务暂时不可用，请稍后再试"));
        } else if (e instanceof ParamFlowException){
            log.warn("热点参数限流: {}", e.getRule().getResource());
            return ResponseEntity.status(HttpStatus.TOO_MANY_REQUESTS)
                    .body(R.fail(HttpStatus.TOO_MANY_REQUESTS.value(), "请求过于频繁，请稍后再试"));
        } else if (e instanceof AuthorityException){
            log.warn("授权规则不通过: {}", e.getRule().getResource());
            return ResponseEntity.status(HttpStatus.FORBIDDEN)
                    .body(R.fail(HttpStatus.FORBIDDEN.value(), "没有权限访问该资源"));
        }

        log.warn("未知限流异常: {}", e.getClass().getName());
        return ResponseEntity.status(HttpStatus.TOO_MANY_REQUESTS)
                .body(R.fail(HttpStatus.TOO_MANY_REQUESTS.value(), "请求被限制，请稍后再试"));
    }
}
